#include "yuvencodethread.h"

YUVEncodeThread::YUVEncodeThread(QObject *parent) : QThread(parent)
{

    connect(this,&QThread::finished,this,&QThread::deleteLater);

}

YUVEncodeThread::~YUVEncodeThread()
{

    disconnect();

    requestInterruption();

    wait();

    quit();

    delete _yuvFileSpec;
    _yuvFileSpec = nullptr;
    qDebug() << "YUVEncodeThread destroy";
}

void YUVEncodeThread::init(YUVFileSpec &yuvFileSpec)
{

    _yuvFileSpec = new YUVFileSpec(yuvFileSpec);
}

void YUVEncodeThread::run()
{

    // 转码
    AVCodec* codec = avcodec_find_encoder_by_name("libx264");

    if(codec == nullptr) {

        qDebug() << "not find libx264";
        return;
    }


    if(!checkFormat(codec,_yuvFileSpec->getFormat())) {

        qDebug() << "format " << av_get_pix_fmt_name(_yuvFileSpec->getFormat());
        return;
    }


    AVCodecContext* codeContext = avcodec_alloc_context3(codec);

    if(codeContext == nullptr) {


        qDebug() << "avcodec_alloc_context3 error";
        return;
    }

    codeContext->width = _yuvFileSpec->getWidth();
    codeContext->height = _yuvFileSpec->getHeight();
    codeContext->pix_fmt = _yuvFileSpec->getFormat();
    codeContext->time_base = {1,_yuvFileSpec->getFps()};


    int res = avcodec_open2(codeContext,codec,nullptr);

    if(res != 0) {

        avcodec_free_context(&codeContext);
        qDebug() << "avcodec_open2 error";
        return;
    }


    AVFrame* frame = av_frame_alloc();

    if(frame == nullptr) {

        avcodec_free_context(&codeContext);

        qDebug() << "av_frame_alloc error";
        return;
    }



    frame->width = _yuvFileSpec->getWidth();
    frame->height = _yuvFileSpec->getHeight();
    frame->format = _yuvFileSpec->getFormat();
    frame->pts = 0;


    res = av_image_alloc(frame->data,frame->linesize,_yuvFileSpec->getWidth(),_yuvFileSpec->getHeight(),_yuvFileSpec->getFormat(),1);

    if(res < 0) {

        avcodec_free_context(&codeContext);

        qDebug() << "av_packet_alloc error";
        return;
    }

    AVPacket* packet = av_packet_alloc();

    if(packet == nullptr) {

        if(frame) {

            av_freep(&frame->data[0]);
            av_frame_free(&frame);
        }
        avcodec_free_context(&codeContext);
        qDebug() << "av_packet_alloc error";
        return;
    }

    _inYuvFile = new QFile(_yuvFileSpec->getInYuvFilePath());

    if(!_inYuvFile->open(QFile::ReadOnly)) {


        if(frame) {

            av_freep(&frame->data[0]);
            av_frame_free(&frame);
        }
        avcodec_free_context(&codeContext);
        qDebug() << _yuvFileSpec->getInYuvFilePath() << "open error";
        return;
    }

    _outYuvFile = new QFile(_yuvFileSpec->getOutYuvFilePath());

    if(!_outYuvFile->open(QFile::WriteOnly)) {

        if(frame) {

            av_freep(&frame->data[0]);
            av_frame_free(&frame);
        }
        avcodec_free_context(&codeContext);
        _inYuvFile->close();
        qDebug() << _yuvFileSpec->getOutYuvFilePath() << "open error";
        return;
    }

    int len = 0;


    while ((len = _inYuvFile->read((char*)frame->data[0],_yuvFileSpec->getImageSize())) > 0) {

        res = yuvEncode(codeContext,frame,packet,_outYuvFile);

        if(res != 0) {

            break;
        }
        frame->pts++;
    }

    yuvEncode(codeContext,nullptr,packet,_outYuvFile);

    _inYuvFile->close();
    _outYuvFile->close();
    av_packet_free(&packet);

    av_freep(&frame->data[0]);
    av_frame_free(&frame);

    avcodec_free_context(&codeContext);
}

bool YUVEncodeThread::checkFormat(AVCodec *codec, AVPixelFormat format)
{

    const enum AVPixelFormat* curFormat = codec->pix_fmts;

    while (*curFormat != AV_PIX_FMT_NONE) {

        if(*curFormat == format) {

            return true;
        }
        curFormat++;
    }

    return false;
}

int YUVEncodeThread::yuvEncode(AVCodecContext *codecContext, AVFrame *frame, AVPacket *packet, QFile* outFile)
{

   int res = avcodec_send_frame(codecContext,frame);

   if(res != 0) {

       qDebug() << "avcodec_send_frame error";
       return res;
   }

   while (true) {


       res = avcodec_receive_packet(codecContext,packet);

       /**
        * Read encoded data from the encoder.
        *
        * @param avctx codec context
        * @param avpkt This will be set to a reference-counted packet allocated by the
        *              encoder. Note that the function will always call
        *              av_packet_unref(avpkt) before doing anything else.
        * @return 0 on success, otherwise negative error code:
        *      AVERROR(EAGAIN):   output is not available in the current state - user
        *                         must try to send input
        *      AVERROR_EOF:       the encoder has been fully flushed, and there will be
        *                         no more output packets
        *      AVERROR(EINVAL):   codec not opened, or it is a decoder
        *      other errors: legitimate encoding errors
        */
       if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

           return 0;
       }else if(res < 0){

           qDebug() << "avcodec_receive_packet error";
           return res;
       }

       outFile->write((char*)packet->data,packet->size);
       av_packet_unref(packet);
   }

}



















